<!DOCTYPE html>

<html>

<head>
    <meta charset="utf-8">
    <title>取点工具</title>
    <style>
        * {
            box-sizing: border-box;
        }

        .bottombar {
            padding: 8px;
        }

        html {
            height: 100%;
        }
            /*body {
                margin: 0;
                display: flex;
                flex-direction: column;
                height: 100%;
            }
            .upperpanel {
                flex-grow: 1;
                overflow: auto;
                padding: 8px;
            }*/
        .upperpanel {
            user-select: none;
        }

        .line {
            position: absolute;
            opacity: 0.75;
            overflow: visible;
            transition: opacity 0.16s ease-out, width 0.16s ease-out, height 0.16s ease-out;
        }

        .line:hover {
            opacity: 1;
        }

        .vline {
            width: 1px;
            top: 0;
            left: 0;
            bottom: 0;
            background: #ffff00;
            cursor: col-resize;
        }
        .vline:hover {
            width: 1.5px;
        }

        .hline {
            height: 1px;
            left: 0;
            right: 0;
            top: 0;
            background: #00ffff;
            cursor: row-resize;
        }
        .hline:hover {
            height: 1.5px;
        }
        .imgbox {
            position: relative;
            width: -moz-fit-content;
            width: fit-content;
        }

        .dragger {
            position: absolute;
        }

        .vline .dragger {
            background: rgba(255, 255, 0, 0.8);
            width: 1vmin;
            min-width: 8px;
            height: 1.5vmin;
            min-height: 16px;
        }

        .hline .dragger {
            background: rgba(0, 255, 255, 0.8);
            width: 1.5vmin;
            min-width: 16px;
            height: 1vmin;
            min-height: 8px;
        }

        #imgelt {
            image-rendering: optimizespeed;
            image-rendering: crisp-edges;
            image-rendering: pixelated;
            
        }
    </style>
</head>

<body>
    <div class="bottombar">
        <input type="file" id="filechooser" accept="image/*" />
        <select id="modesel">
            <option value="abs">绝对坐标</option>
            <option value="vwvh">宽高相对坐标</option>
            <option value="leftvh">高度相对坐标（左对齐）</option>
            <option value="midvh">高度相对坐标（居中）</option>
            <option value="rightvh">高度相对坐标（右对齐）</option>
        </select>
        <button id="cropbtn">crop</button>
        <button id="resetbtn">reset</button>
        <pre>(<span id="leftpos">0</span>, <span id="toppos">0</span>, <span id="rightpos">0</span>, <span id="bottompos">0</span>)</pre>
    </div>
    <div class="upperpanel">
        <div class="imgbox">
            <img id="imgelt" src="" draggable="false" crossorigin="anonymous" />
            <div class="line hline" id="hline1">
                <div class="dragger"></div>
            </div>
            <div class="line hline" id="hline2" style="transform: translateY(40px)">
                <div class="dragger"></div>
            </div>
            <div class="line vline" id="vline1">
                <div class="dragger"></div>
            </div>
            <div class="line vline" id="vline2" style="transform: translateX(40px)">
                <div class="dragger"></div>
            </div>
        </div>
    </div>
    <img id="cropimg" src="data:," crossorigin="anonymous"/>
    <script>
        let $ = (x) => document.querySelector(x)
        let $$ = (x) => document.querySelectorAll(x)
        let changefile = function () { }
        $('#filechooser').addEventListener('filechanged', function () {
            changefile()
        })

        let absleft, abstop, absright, absbottom

        //              [vw, vh, 1]
        let leftmat = [0, 0, 0]
        let rightmat = [0, 0, 40]
        let topmat = [0, 0, 0]
        let bottommat = [0, 0, 40]

        let format = function (base, num, unit) {
            if (num == 0) return base === '' ? '0' : base
            if (num > 0) return base === '' ? num.toFixed(3) + '*' + unit : base + '+' + num.toFixed(3) + '*' + unit
            if (num < 0) return base + num.toFixed(3) + '*' + unit
        }

        let updateOutput = function () {
            let imgelt = $('#imgelt')
            let vwidth = imgelt.width
            let vheight = imgelt.height

            let hline1top = $('#hline1').getBoundingClientRect().top - imgelt.getBoundingClientRect().top
            let hline2top = $('#hline2').getBoundingClientRect().top - imgelt.getBoundingClientRect().top;
            [abstop, absbottom] = [hline1top, hline2top].sort((x, y) => x - y)

            let vline1left = $('#vline1').getBoundingClientRect().left - imgelt.getBoundingClientRect().left
            let vline2left = $('#vline2').getBoundingClientRect().left - imgelt.getBoundingClientRect().left;
            [absleft, absright] = [vline1left, vline2left].sort((x, y) => x - y)

            let mode = $('#modesel').selectedOptions[0].value
            if (mode === 'abs') {
                $('#leftpos').innerText = absleft.toString()
                $('#toppos').innerText = abstop.toString()
                $('#rightpos').innerText = absright.toString()
                $('#bottompos').innerText = absbottom.toString()
                leftmat = [0, 0, absleft]
                rightmat = [0, 0, absright]
                topmat = [0, 0, abstop]
                bottommat = [0, 0, absbottom]
                return
            }
            let topvh = abstop / vheight * 100
            let bottomvh = absbottom / vheight * 100
            $('#toppos').innerText = format('', topvh, 'vh')
            $('#bottompos').innerText = format('', bottomvh, 'vh')
            topmat = [0, topvh, 0]
            bottommat = [0, bottomvh, 0]

            switch (mode) {
                case 'vwvh': {
                    let leftvw = absleft / vwidth * 100
                    let rightvw = absright / vwidth * 100
                    $('#leftpos').innerText = format('', leftvw, 'vw')
                    $('#rightpos').innerText = format('', rightvw, 'vw')
                    leftmat = [leftvw, 0, 0]
                    rightmat = [rightvw, 0, 0]
                    break
                }
                case 'leftvh': {
                    let leftvh = absleft / vheight * 100
                    let rightvh = absright / vheight * 100
                    $('#leftpos').innerText = format('', leftvh, 'vh')
                    $('#rightpos').innerText = format('', rightvh, 'vh')
                    leftmat = [0, leftvh, 0]
                    rightmat = [0, rightvh, 0]
                    break
                }
                case 'midvh': {
                    let leftvh = (absleft - vwidth / 2) / vheight * 100
                    let rightvh = (absright - vwidth / 2) / vheight * 100

                    $('#leftpos').innerText = format('50*vw', leftvh, 'vh')
                    $('#rightpos').innerText = format('50*vw', rightvh, 'vh')
                    leftmat = [50, leftvh, 0]
                    rightmat = [50, rightvh, 0]
                    break
                }
                case 'rightvh': {
                    let leftvh = (absleft - vwidth) / vheight * 100
                    let rightvh = (absright - vwidth) / vheight * 100

                    $('#leftpos').innerText = format('100*vw', leftvh, 'vh')
                    $('#rightpos').innerText = format('100*vw', rightvh, 'vh')
                    leftmat = [100, leftvh, 0]
                    rightmat = [100, rightvh, 0]
                    break
                }
            }
        }

        let setX = function (elm, x) {
            elm.style.transform = 'translateX(' + x.toString() + 'px)'
        }

        let setY = function (elm, x) {
            elm.style.transform = 'translateY(' + x.toString() + 'px)'
        }

        let updateLayout = function () {
            let imgelt = $('#imgelt')
            let vw = imgelt.width / 100
            let vh = imgelt.height / 100

            let mat3mul = function (mat1, mat2) {
                return mat1[0] * mat2[0] + mat1[1] * mat2[1] + mat1[2] * mat2[2]
            }
            setX($('#vline1'), mat3mul(leftmat, [vw, vh, 1]))
            setX($('#vline2'), mat3mul(rightmat, [vw, vh, 1]))
            setY($('#hline1'), mat3mul(topmat, [vw, vh, 1]))
            setY($('#hline2'), mat3mul(bottommat, [vw, vh, 1]))
            updateOutput()
        }


        for (let elt of $$('.vline')) {
            elt.addEventListener('mousedown', function (e) {
                e.preventDefault()
                this.draginitX = e.pageX
                let elm = elt
                let movehandler = function (e) {
                    e.preventDefault()
                    let offsetX = (e.pageX - elm.draginitX)
                    let originaltranslateX = parseFloat(elm.style.transform.replace('translateX(', ''))
                    if (isNaN(originaltranslateX)) originaltranslateX = 0
                    let eltX = (originaltranslateX + offsetX).toString() + 'px'
                    elm.style.transform = 'translateX(' + eltX.toString() + ')'
                    elm.draginitX = e.pageX
                    updateOutput()
                }
                document.addEventListener('mousemove', movehandler)
                let uphandler = function (e) {
                    e.preventDefault()
                    document.removeEventListener('mousemove', movehandler)
                    document.removeEventListener('mouseup', uphandler)
                }
                document.addEventListener('mouseup', uphandler)
            })
        }
        for (let elt of $$('.hline')) {
            elt.addEventListener('mousedown', function (e) {
                e.preventDefault()
                this.draginitY = e.pageY
                let elm = elt
                let movehandler = function (e) {
                    e.preventDefault()
                    let offsetY = (e.pageY - elm.draginitY)
                    let originaltranslateY = parseFloat(elm.style.transform.replace('translateY(', ''))
                    if (isNaN(originaltranslateY)) originaltranslateY = 0
                    let eltY = (originaltranslateY + offsetY).toString() + 'px'
                    elm.style.transform = 'translateY(' + eltY.toString() + ')'
                    elm.draginitY = e.pageY
                    updateOutput()
                }
                document.addEventListener('mousemove', movehandler)
                let uphandler = function (e) {
                    e.preventDefault()
                    document.removeEventListener('mousemove', movehandler)
                    document.removeEventListener('mouseup', uphandler)
                }
                document.addEventListener('mouseup', uphandler)
            })
        }
        $('#imgelt').addEventListener('load', () => {
            updateLayout()
        })
        $('#modesel').addEventListener('change', () => updateOutput())

        let loadImage = function(file) {
            if($('#imgelt').src) {
                URL.revokeObjectURL($('#imgelt').src)
            }
            $('#imgelt').src = URL.createObjectURL(file)
        }

        $('#filechooser').addEventListener('change', function(e) {
            loadImage(e.target.files[0])
        })


        let createCanvas = function(w, h) {
            let canvas = document.createElement('canvas')
            canvas.width = w
            canvas.height = h
            return canvas
        }
        let offscreenCanvasToBlob = function(c) {
            return new Promise((resolve, reject)=>{
                c.toBlob(resolve)
            })
        }

        $('#cropbtn').addEventListener('click', async function(){
            let width = absright - absleft
            let height = absbottom - abstop
            let canvas = createCanvas(width, height)
            let ctx = canvas.getContext('2d')
            ctx.drawImage($('#imgelt'), absleft, abstop, width, height, 0, 0, width, height)
            // let blob = await offscreenCanvasToBlob(canvas)
            // if($('#cropimg').src) {
            //     URL.revokeObjectURL($('#cropimg').src)
            // }
            canvas.toBlob(blob=>{
                if($('#cropimg').src) {
                    URL.revokeObjectURL($('#cropimg').src)
                }
                $('#cropimg').src = URL.createObjectURL(blob)
                $('#cropimg').scrollIntoView(true)
            })
        })

        $('#resetbtn').addEventListener('click', function(){
            leftmat = [0, 0, 0]
            rightmat = [0, 0, 40]
            topmat = [0, 0, 0]
            bottommat = [0, 0, 40]
            updateLayout();
            updateOutput();
        })

        let preventDefaults = e => {
            e.preventDefault()
            e.stopPropagation()
        }

        ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            document.addEventListener(eventName, preventDefaults, false)
        })

        document.addEventListener('drop', function(e) {
            let dt = e.dataTransfer
            let files = dt.files
            console.log(files)
            if (files.length >= 1) {
                loadImage(files[0])
            }
        }, false)
    </script>
</body>

</html>
